/**
 * @file    drv_flash.c
 * @author  yhy
 * @brief   flash抽象层
 * @version 1.01
 * @date    2025-08-13
 *
 * @copyright Copyright (c) 2025
 *
 * @details
 * Change Logs:
 * Date           Author       Notes
 * 2025-08-13     yhy          the first version V1.00
 * 2025-08-26     yhy          add get sram size function V1.01
 *
 */
#include "drv_flash.h"

#include "flash_drv.h"

#include <string.h>

static result_e snf_drv_flash_verify_page_addr(uint32_t page_addr);
static result_e snf_drv_flash_write_2word(uint32_t addr, uint32_t data_low, uint32_t data_high);
static result_e snf_drv_flash_read(uint32_t addr, uint8_t *read_buf, uint32_t size);
static result_e snf_drv_flash_lock(snf_flash_area_type_e flash_area);
static result_e snf_drv_flash_unlock(snf_flash_area_type_e flash_area);
static result_e snf_drv_flash_erase_page(uint32_t page_addr);
static result_e snf_drv_flash_cfg_pflash_init(void);
static result_e snf_drv_flash_cfg_pflash_read(long offset, uint8_t *buf, size_t size);
static result_e snf_drv_flash_cfg_pflash_write(long offset, const uint8_t *buf, size_t size);
static result_e snf_drv_flash_cfg_pflash_erase(long offset, size_t size);
static result_e snf_drv_flash_cfg_dflash_init(void);
static result_e snf_drv_flash_cfg_dflash_read(long offset, uint8_t *buf, size_t size);
static result_e snf_drv_flash_cfg_dflash_write(long offset, const uint8_t *buf, size_t size);
static result_e snf_drv_flash_cfg_dflash_erase(long offset, size_t size);

/* ===================== 设备表和分区表 ===================== */

/* Flash设备表 */
const snf_flash_dev_t snf_flash_table[] = {
    {
        .name = FLASH_DEV_NAME_PFLASH,
        .base_addr = FLASH_PFLASH_BASE_ADDR,
        .len = FLASH_PFLASH_SIZE,
        .erase_blk_size = FLASH_PAGE_SIZE,
        .ops_t = {
            .init = snf_drv_flash_cfg_pflash_init,
            .read = snf_drv_flash_cfg_pflash_read,
            .write = snf_drv_flash_cfg_pflash_write,
            .erase = snf_drv_flash_cfg_pflash_erase,
        },
    },
    {
        .name = FLASH_DEV_NAME_DFLASH,
        .base_addr = FLASH_DFLASH_BASE_ADDR,
        .len = FLASH_DFLASH_SIZE,
        .erase_blk_size = FLASH_PAGE_SIZE,
        .ops_t = {
            .init = snf_drv_flash_cfg_dflash_init,
            .read = snf_drv_flash_cfg_dflash_read,
            .write = snf_drv_flash_cfg_dflash_write,
            .erase = snf_drv_flash_cfg_dflash_erase,
        },
    },
};

/* 设备表长度 */
const size_t snf_flash_table_len = sizeof(snf_flash_table) / sizeof(snf_flash_table[0]);

/* 分区表定义 */
const snf_flash_partition_t snf_partition_table[] = {
    {
        .magic_word = FLASH_PART_MAGIC_WORD,
        .name       = FLASH_PART_NAME_APP1,
        .flash_name = FLASH_DEV_NAME_PFLASH,
        .offset     = 0,
        .len        = 256 * 1024, /* 256KB */
        .reserved   = 0,
    },
    {
        .magic_word = FLASH_PART_MAGIC_WORD,
        .name       = FLASH_PART_NAME_APP2,
        .flash_name = FLASH_DEV_NAME_PFLASH,
        .offset     = 256 * 1024,
        .len        = 256 * 1024, /* 256KB */
        .reserved   = 0,
    },
    {
        .magic_word = FLASH_PART_MAGIC_WORD,
        .name       = FLASH_PART_NAME_DATA,
        .flash_name = FLASH_DEV_NAME_DFLASH,
        .offset     = 0,
        .len        = 128 * 1024, /* 128KB */
        .reserved   = 0,
    },
};

const size_t snf_partition_table_len = sizeof(snf_partition_table) / sizeof(snf_partition_table[0]);

/**
 * @brief 初始化Flash驱动
 *
 * @return result_e 错误码
 */
result_e snf_drv_flash_init(void)
{
    for (size_t i = 0; i < snf_flash_table_len; i++) {
        if (snf_flash_table[i].ops_t.init) {
            result_e result = snf_flash_table[i].ops_t.init();
            if (result != RESULT_STATUS_OK) {
                return RESULT_STATUS_FLASH_ERR_GENERAL;
            }
        }
    }

    return RESULT_STATUS_OK;
}

/**
 * @brief 根据名称查找Flash设备
 *
 * @param   name                    FLASH设备名称
 * @return  const snf_flash_dev_t*  设备指针, 未找到返回NULL
 */
const snf_flash_dev_t *snf_drv_flash_device_find(const char *name)
{
    if (!name) {
        return NULL;
    }

    for (size_t i = 0; i < snf_flash_table_len; i++) {
        if (strcmp(name, snf_flash_table[i].name) == 0) {
            return &snf_flash_table[i];
        }
    }

    return NULL;
}

/**
 * @brief 根据索引获取Flash设备
 *
 * @param   index                    Flash设备索引
 * @return  const snf_flash_dev_t*   设备指针, 未找到返回NULL
 */
const snf_flash_dev_t *snf_drv_flash_device_get(size_t index)
{
    if (index >= snf_flash_table_len) {
        return NULL;
    }

    return &snf_flash_table[index];
}

/**
 * @brief 根据名称查找分区
 *
 * @param   name                            分区名称
 * @return  const snf_flash_partition_t*    分区指针, 未找到返回NULL
 */
const snf_flash_partition_t *snf_drv_flash_partition_find(const char *name)
{
    if (!name) {
        return NULL;
    }

    for (size_t i = 0; i < snf_partition_table_len; i++) {
        if (strcmp(name, snf_partition_table[i].name) == 0) {
            return &snf_partition_table[i];
        }
    }

    return NULL;
}

/**
 * @brief 根据索引获取分区
 *
 * @param index                         分区索引
 * @return const snf_flash_partition_t* 分区指针, 未找到返回NULL
 */
const snf_flash_partition_t *snf_drv_flash_partition_get(size_t index)
{
    if (index >= snf_partition_table_len) {
        return NULL;
    }

    return &snf_partition_table[index];
}

/**
 * @brief 从分区读取数据
 *
 * @param part                      分区指针
 * @param addr                      分区内偏移地址
 * @param buf                       读取数据缓存指针
 * @param size                      读取数据大小
 * @return result_e     错误码
 */
result_e snf_drv_flash_partition_read(const snf_flash_partition_t *part, uint32_t addr, uint8_t *buf,
                                      size_t size)
{
    if (!part || !buf || size == 0) {
        return RESULT_STATUS_FLASH_ERR_INVALID_PARAM;
    }

    /* 判断偏移量是否超出分区范围 */
    if (addr + size > part->len) {
        return RESULT_STATUS_FLASH_ERR_INVALID_PARAM;
    }

    const snf_flash_dev_t *flash_dev = snf_drv_flash_device_find(part->flash_name);
    if (!flash_dev) {
        return RESULT_STATUS_FLASH_ERR_NOT_FOUND;
    }

    result_e result = flash_dev->ops_t.read(part->offset + addr, buf, size);
    if (result == RESULT_STATUS_OK) {
        return RESULT_STATUS_FLASH_ERR_OK;
    }

    return RESULT_STATUS_FLASH_ERR_GENERAL;
}

/**
 * @brief 向分区写入数据
 *
 * @param part                      分区指针
 * @param addr                      分区内偏移地址
 * @param buf                       写入数据缓存指针
 * @param size                      写入数据大小
 * @return result_e     错误码
 */
result_e snf_drv_flash_partition_write(const snf_flash_partition_t *part, uint32_t addr, const uint8_t *buf,
                                       size_t size)
{
    if (!part || !buf || size == 0) {
        return RESULT_STATUS_FLASH_ERR_INVALID_PARAM;
    }

    /* 判断偏移量是否超出分区范围 */
    if (addr + size > part->len) {
        return RESULT_STATUS_FLASH_ERR_INVALID_PARAM;
    }

    const snf_flash_dev_t *flash_dev = snf_drv_flash_device_find(part->flash_name);
    if (!flash_dev) {
        return RESULT_STATUS_FLASH_ERR_NOT_FOUND;
    }

    result_e result = flash_dev->ops_t.write(part->offset + addr, buf, size);
    if (result == RESULT_STATUS_OK) {
        return RESULT_STATUS_FLASH_ERR_OK;
    }

    return RESULT_STATUS_FLASH_ERR_GENERAL;
}

/**
 * @brief 擦除分区数据
 *
 * @param part                      分区指针
 * @param addr                      分区内偏移地址
 * @param size                      擦除数据大小
 * @return result_e     错误码
 */
result_e snf_drv_flash_partition_erase(const snf_flash_partition_t *part, uint32_t addr, size_t size)
{
    if (!part || size == 0) {
        return RESULT_STATUS_FLASH_ERR_INVALID_PARAM;
    }

    /* 判断偏移量是否超出分区范围 */
    if (addr + size > part->len) {
        return RESULT_STATUS_FLASH_ERR_INVALID_PARAM;
    }

    const snf_flash_dev_t *flash_dev = snf_drv_flash_device_find(part->flash_name);
    if (!flash_dev) {
        return RESULT_STATUS_FLASH_ERR_NOT_FOUND;
    }

    result_e result = flash_dev->ops_t.erase(part->offset + addr, size);
    if (result == RESULT_STATUS_OK) {
        return RESULT_STATUS_FLASH_ERR_OK;
    }

    return RESULT_STATUS_FLASH_ERR_GENERAL;
}

/**
 * @brief 擦除整个分区数据
 *
 * @param part                      分区指针
 * @return result_e     错误码
 */
result_e snf_drv_flash_partition_erase_all(const snf_flash_partition_t *part)
{
    if (!part) {
        return RESULT_STATUS_FLASH_ERR_INVALID_PARAM;
    }

    return snf_drv_flash_partition_erase(part, 0, part->len);
}

/**
 * @brief 直接操作P-Flash读取
 *
 * @param offset                    偏移地址
 * @param buf                       读取缓存
 * @param size                      读取大小
 * @return result_e     错误码
 */
result_e snf_drv_flash_pflash_read(uint32_t offset, uint8_t *buf, size_t size)
{
    uint32_t addr   = FLASH_PFLASH_BASE_ADDR + offset;
    result_e result = snf_drv_flash_read(addr, buf, size);

    if (result == RESULT_STATUS_OK) {
        return RESULT_STATUS_FLASH_ERR_OK;
    }

    return RESULT_STATUS_FLASH_ERR_GENERAL;
}

/**
 * @brief 直接操作P-Flash写入
 *
 * @param offset                    偏移地址
 * @param buf                       写入数据
 * @param size                      写入大小
 * @return result_e     错误码
 */
result_e snf_drv_flash_pflash_write(uint32_t offset, const uint8_t *buf, size_t size)
{
    const snf_flash_dev_t *flash_dev = snf_drv_flash_device_find(FLASH_DEV_NAME_PFLASH);
    if (!flash_dev) {
        return RESULT_STATUS_FLASH_ERR_NOT_FOUND;
    }

    result_e result = flash_dev->ops_t.write(offset, buf, size);
    if (result == RESULT_STATUS_OK) {
        return RESULT_STATUS_FLASH_ERR_OK;
    }

    return RESULT_STATUS_FLASH_ERR_GENERAL;
}

/**
 * @brief 直接操作P-Flash擦除
 *
 * @param offset                    偏移地址
 * @param size                      擦除大小
 * @return result_e     错误码
 */
result_e snf_drv_flash_pflash_erase(uint32_t offset, size_t size)
{
    const snf_flash_dev_t *flash_dev = snf_drv_flash_device_find(FLASH_DEV_NAME_PFLASH);
    if (!flash_dev) {
        return RESULT_STATUS_FLASH_ERR_NOT_FOUND;
    }

    result_e result = flash_dev->ops_t.erase(offset, size);
    if (result == RESULT_STATUS_OK) {
        return RESULT_STATUS_FLASH_ERR_OK;
    }

    return RESULT_STATUS_FLASH_ERR_GENERAL;
}

/**
 * @brief 直接操作D-Flash读取
 *
 * @param offset                    偏移地址
 * @param buf                       读取缓存
 * @param size                      读取大小
 * @return result_e     错误码
 */
result_e snf_drv_flash_dflash_read(uint32_t offset, uint8_t *buf, size_t size)
{
    uint32_t addr   = FLASH_DFLASH_BASE_ADDR + offset;
    result_e result = snf_drv_flash_read(addr, buf, size);

    if (result == RESULT_STATUS_OK) {
        return RESULT_STATUS_FLASH_ERR_OK;
    }

    return RESULT_STATUS_FLASH_ERR_GENERAL;
}

/**
 * @brief 直接操作D-Flash写入
 *
 * @param offset                    偏移地址
 * @param buf                       写入数据
 * @param size                      写入大小
 * @return result_e     错误码
 */
result_e snf_drv_flash_dflash_write(uint32_t offset, const uint8_t *buf, size_t size)
{
    const snf_flash_dev_t *flash_dev = snf_drv_flash_device_find(FLASH_DEV_NAME_DFLASH);
    if (!flash_dev) {
        return RESULT_STATUS_FLASH_ERR_NOT_FOUND;
    }

    result_e result = flash_dev->ops_t.write(offset, buf, size);
    if (result == RESULT_STATUS_OK) {
        return RESULT_STATUS_FLASH_ERR_OK;
    }

    return RESULT_STATUS_FLASH_ERR_GENERAL;
}

/**
 * @brief 直接操作D-Flash擦除
 *
 * @param offset                    偏移地址
 * @param size                      擦除大小
 * @return result_e     错误码
 */
result_e snf_drv_flash_dflash_erase(uint32_t offset, size_t size)
{
    const snf_flash_dev_t *flash_dev = snf_drv_flash_device_find(FLASH_DEV_NAME_DFLASH);
    if (!flash_dev) {
        return RESULT_STATUS_FLASH_ERR_NOT_FOUND;
    }

    result_e result = flash_dev->ops_t.erase(offset, size);
    if (result == RESULT_STATUS_OK) {
        return RESULT_STATUS_FLASH_ERR_OK;
    }

    return RESULT_STATUS_FLASH_ERR_GENERAL;
}

/**
 * @brief 获取SRAM大小
 *
 * @return uint16_t SRAM大小
 */
uint16_t snf_drv_flash_get_sram_size(void)
{
    uint16_t pflash_size, sram_size = 0;
    FLASH_GetChipMemorySizeInfo(&pflash_size, &sram_size);

    return sram_size;
}

/* PFLASH操作函数实现 */
static result_e snf_drv_flash_cfg_pflash_init(void) { return RESULT_STATUS_OK; }

static result_e snf_drv_flash_cfg_pflash_read(long offset, uint8_t *buf, size_t size)
{
    uint32_t addr = FLASH_PFLASH_BASE_ADDR + offset;
    return snf_drv_flash_read(addr, buf, size);
}

static result_e snf_drv_flash_cfg_pflash_write(long offset, const uint8_t *buf, size_t size)
{
    uint32_t addr = FLASH_PFLASH_BASE_ADDR + offset;
    result_e result;

    /* 检查地址和大小对齐 */
    if ((addr & 0x07) != 0 || (size & 0x07) != 0) {
        return RESULT_STATUS_INVALID_PARAM;
    }

    /* 解锁FLASH */
    result = snf_drv_flash_unlock(FLASH_AREA_TYPE_PFLASH);
    if (result != RESULT_STATUS_OK) {
        return result;
    }

    /* 按8字节为单位写入 */
    for (size_t i = 0; i < size; i += 8) {
        uint32_t data_low  = *(uint32_t *)(buf + i);
        uint32_t data_high = *(uint32_t *)(buf + i + 4);

        result = snf_drv_flash_write_2word(addr + i, data_low, data_high);
        if (result != RESULT_STATUS_OK) {
            snf_drv_flash_lock(FLASH_AREA_TYPE_PFLASH);
            return result;
        }
    }

    /* 锁定PFLASH */
    snf_drv_flash_lock(FLASH_AREA_TYPE_PFLASH);
    return RESULT_STATUS_OK;
}

static result_e snf_drv_flash_cfg_pflash_erase(long offset, size_t size)
{
    uint32_t addr = FLASH_PFLASH_BASE_ADDR + offset;
    result_e result;

    /* 检查地址和大小对齐 */
    if ((offset & (FLASH_PAGE_SIZE - 1)) != 0 || (size & (FLASH_PAGE_SIZE - 1)) != 0) {
        return RESULT_STATUS_INVALID_PARAM;
    }

    /* 解锁PFLASH */
    result = snf_drv_flash_unlock(FLASH_AREA_TYPE_PFLASH);
    if (result != RESULT_STATUS_OK) {
        return result;
    }

    /* 按页擦除 */
    for (size_t i = 0; i < size; i += FLASH_PAGE_SIZE) {
        result = snf_drv_flash_erase_page(addr + i);
        if (result != RESULT_STATUS_OK) {
            snf_drv_flash_lock(FLASH_AREA_TYPE_PFLASH);
            return result;
        }
    }

    /* 锁定PFLASH */
    snf_drv_flash_lock(FLASH_AREA_TYPE_PFLASH);
    return RESULT_STATUS_OK;
}

/* DFLASH操作函数实现 */
static result_e snf_drv_flash_cfg_dflash_init(void) { return RESULT_STATUS_OK; }

static result_e snf_drv_flash_cfg_dflash_read(long offset, uint8_t *buf, size_t size)
{
    uint32_t addr = FLASH_DFLASH_BASE_ADDR + offset;
    return snf_drv_flash_read(addr, buf, size);
}

static result_e snf_drv_flash_cfg_dflash_write(long offset, const uint8_t *buf, size_t size)
{
    uint32_t addr = FLASH_DFLASH_BASE_ADDR + offset;
    result_e result;

    /* 检查地址和大小对齐 */
    if ((addr & 0x07) != 0 || (size & 0x07) != 0) {
        return RESULT_STATUS_INVALID_PARAM;
    }

    /* 解锁DFLASH */
    result = snf_drv_flash_unlock(FLASH_AREA_TYPE_DFLASH);
    if (result != RESULT_STATUS_OK) {
        return result;
    }

    /* 按8字节为单位写入 */
    for (size_t i = 0; i < size; i += 8) {
        uint32_t data_low  = *(uint32_t *)(buf + i);
        uint32_t data_high = *(uint32_t *)(buf + i + 4);

        result = snf_drv_flash_write_2word(addr + i, data_low, data_high);
        if (result != RESULT_STATUS_OK) {
            snf_drv_flash_lock(FLASH_AREA_TYPE_DFLASH);
            return result;
        }
    }

    /* 锁定DFLASH */
    snf_drv_flash_lock(FLASH_AREA_TYPE_DFLASH);
    return RESULT_STATUS_OK;
}

static result_e snf_drv_flash_cfg_dflash_erase(long offset, size_t size)
{
    uint32_t addr = FLASH_DFLASH_BASE_ADDR + offset;
    result_e result;

    /* 检查地址和大小对齐 */
    if ((offset & (FLASH_PAGE_SIZE - 1)) != 0 || (size & (FLASH_PAGE_SIZE - 1)) != 0) {
        return RESULT_STATUS_INVALID_PARAM;
    }

    /* 解锁DFLASH */
    result = snf_drv_flash_unlock(FLASH_AREA_TYPE_DFLASH);
    if (result != RESULT_STATUS_OK) {
        return result;
    }

    /* 按页擦除 */
    for (size_t i = 0; i < size; i += FLASH_PAGE_SIZE) {
        result = snf_drv_flash_erase_page(addr + i);
        if (result != RESULT_STATUS_OK) {
            snf_drv_flash_lock(FLASH_AREA_TYPE_DFLASH);
            return result;
        }
    }

    /* 锁定DFLASH */
    snf_drv_flash_lock(FLASH_AREA_TYPE_DFLASH);
    return RESULT_STATUS_OK;
}

/**
 * @brief 向Flash指定区域写入2word数据, IM94x系列最小编程单位为2word
 *
 * @param   addr        待写入的Flash地址
 * @param   data_low    待写入的数据低4字节
 * @param   data_high   待写入的数据高4字节
 * @return  result_e    写入结果, 返回RESULT_STATUS_OK表示成功, 其他表示失败
 */
static result_e snf_drv_flash_write_2word(uint32_t addr, uint32_t data_low, uint32_t data_high)
{
    result_e result = snf_drv_flash_verify_page_addr(addr);
    if (result != RESULT_STATUS_OK) {
        return result;
    }

    if (FLASH_Program(addr, data_low, data_high) != ERR_SUCCESS) {
        return RESULT_STATUS_FLASH_WRITE_FAIL;
    }

    return RESULT_STATUS_OK;
}

/**
 * @brief 读取Flash指定区域的数据
 *
 * @param   addr        待读取的Flash地址
 * @param   read_buf    读取数据存放的缓存区
 * @param   size        读取数据大小
 * @return  result_e    读取结果, 返回RESULT_STATUS_OK表示成功, 其他表示失败
 */
static result_e snf_drv_flash_read(uint32_t addr, uint8_t *read_buf, uint32_t size)
{
    result_e result = snf_drv_flash_verify_page_addr(addr);
    if (result != RESULT_STATUS_OK) {
        return result;
    }

    FLASH_Read(addr, read_buf, size);

    return RESULT_STATUS_OK;
}

/**
 * @brief 对指定区域的Flash进行加锁
 *
 * @param   flash_area  Flash区域
 * @return  result_e    加锁结果, 返回RESULT_STATUS_OK表示成功, 其他表示失败
 */
static result_e snf_drv_flash_lock(snf_flash_area_type_e flash_area)
{
    if (flash_area >= FLASH_AREA_TYPE_MAX) {
        return RESULT_STATUS_INVALID_PARAM;
    }

    FLASH_Lock((flash_area_type_t)flash_area);

    return RESULT_STATUS_OK;
}

/**
 * @brief 对指定区域的Flash进行解锁
 *
 * @param   flash_area  Flash区域
 * @return  result_e    解锁结果, 返回RESULT_STATUS_OK表示成功, 其他表示失败
 */
static result_e snf_drv_flash_unlock(snf_flash_area_type_e flash_area)
{
    if (flash_area >= FLASH_AREA_TYPE_MAX) {
        return RESULT_STATUS_INVALID_PARAM;
    }

    FLASH_Unlock((flash_area_type_t)flash_area);

    return RESULT_STATUS_OK;
}

/**
 * @brief 擦除FLASH指定页
 *
 * @param   page_addr   页起始地址
 * @return  result_e    擦除结果, 返回RESULT_STATUS_OK表示成功, 其他表示失败
 */
static result_e snf_drv_flash_erase_page(uint32_t page_addr)
{
    result_e result = snf_drv_flash_verify_page_addr(page_addr);
    if (result != RESULT_STATUS_OK) {
        return result;
    }

    if (FLASH_ErasePage(page_addr) != ERR_SUCCESS) {
        return RESULT_STATUS_FLASH_ERASE_PAGE_FAIL;
    }

    return RESULT_STATUS_OK;
}

/**
 * @brief 校验flash页地址是否合法
 *
 * @param   page_addr   flash页地址
 * @return  result_e    校验结果, 返回RESULT_STATUS_OK表示合法, 其他表示非法
 */
static result_e snf_drv_flash_verify_page_addr(uint32_t page_addr)
{
    if ((page_addr >= FLASH_PFLASH_BASE_ADDR && page_addr <= FLASH_PFLASH_END_ADDR) ||
        (page_addr >= FLASH_DFLASH_BASE_ADDR && page_addr <= FLASH_DFLASH_END_ADDR)) {
        return RESULT_STATUS_OK;
    }

    return RESULT_STATUS_FLASH_PAGE_ADDR_OVERSTEP;
}
